/**
 * SCPI LUA 接口
 */

#include <lua.h>
#include <lauxlib.h>
#include <string.h>

#include "share/lua_utils.h"
#include "scpi_lexer.h"

#include "share/log.h"
// 在包含用户日志头文件 "log4app.h" 之前，应先定义 LOGLEVEL 和 LOGTAG
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "SCPI-LUA"
#include "share/log4app.h"

void lua_push_scpi_cmdgroup(lua_State* L, const scpi_cmd_group_t* group) {
	if (!L || !group) return;
	const scpi_cmd_t* cmd;
	// LUA 数组下标从 1 开始
	int idx = 1;

	// cmds 数组
	lua_newtable(L);

	for (int ci = 0; ci < group->cmd_count; ci++) {
		cmd = &(group->cmds[ci]);

		// 新建 cmd 对象
		lua_newtable(L);

		/* 填写 cmd.parts 数组 */ {
			// 新建 cmd parts 数组
			lua_newtable(L); idx = 1;

			// 如果命令是 '*' 通用命令，则把 '*' 命名为 "asterisk"，做为第一个 cmd part
			if (cmd->is_asterisk) { lua_pushstring(L, "asterisk"); lua_rawseti(L, -2, idx++); }

			// 填写其它 cmd parts
			for (int pi = 0; pi < cmd->part_count; pi++) {
				lua_pushstring(L, cmd->parts[pi].str); lua_rawseti(L, -2, idx++);
			}

			// 如果命令是 '?' 查询命令，则添加一个 "query"，做为最后一个 cmd part
			if (cmd->is_query) { lua_pushstring(L, "query"); lua_rawseti(L, -2, idx++); }

			// 把 cmd parts 数组做为 "parts" 字段放入 cmd 对象
			lua_setfield(L, -2, "parts");
		}

		/* 填写 cmd.args 数组 */ {
			// 新建 cmd args 数组
			lua_newtable(L);

			// 填写 cmd args
			for (int ai = 0; ai < cmd->arg_count; ai++) {
				// 把 arg.str 放入 args 数组
				lua_pushstring(L, cmd->args[ai].str);
				lua_rawseti(L, -2, ai+1);
			}

			// 把 cmd args 数组做为 "args" 字段放入 cmd 对象
			lua_setfield(L, -2, "args");
		}

		// 把 cmd 对象放入 cmds 数组
		lua_rawseti(L, -2, ci+1);
	}

	// cmds 数组保留在 LUA 栈上 ...
}

/**
* 准备 SCPI 指令字符串
* yybuf, err = scpi.parse_prepare(pointer, len)    -- 要解析的 SCPI 指令字符串在 (lightuserdata 类型的) pointer 指定的内存中，长度为 len
* yybuf, err = scpi.parse_prepare(userdata[, len]) -- 要解析的 SCPI 指令字符串在 （"cmem" 型的） userdata 中，长度为 len，如果 len == nil，则取 userdata 本身大小
* yybuf, err = scpi.parse_prepare(string[, len])   -- 要解析的 SCPI 指令字符串是一个二进制字符串，长度为 len，如果 len == nil，则取字符串本身大小
* @return yybuf, err 正常情况下，返回 lightuserdata 型 yybuf，err 为 nil; 解析或 SCPI 指令执行过程中遇到错误，则 yybuf 为 nil, err 为 错误信息
*/
static int _scpi_parse_prepare(lua_State* L) {
	size_t size = 0, dlen = 0;
	void* buf = NULL;
	void* yybuf = NULL;
	std_err_t err = STD_ERR_OK;

	// 检出 buffer 参数
	luaL_checkbuffer(L, 1, buf, size, dlen);

	err = scpi_parse_prepare(buf, dlen, &yybuf);
	if (err) {
		lua_pushnil(L);
		lua_pushstring(L, std_err_comment(err));
		return 2;
	}

	lua_pushlightuserdata(L, yybuf);
	return 1;
}

/**
* 解析 SCPI
* cmdgroup, err = scpi.parse_next(yybuf)
* @return cmdgroup, err 
* @retval 正常情况下，返回解析出来的命令组 cmdgroup，err 为 nil; 如果 cmdgroup 为 nil，表示所有指令解析完成。
* @retval 解析或 SCPI 指令执行过程中遇到错误，则 cmdgroup 为 nil, err 为 错误信息。
*/
static int _scpi_parse_next(lua_State* L) {
	scpi_cmd_group_t* cmds = NULL;
	std_err_t err = scpi_parse_next(&cmds);
	if (err) {
		lua_pushnil(L);
		lua_pushstring(L, std_err_comment(err));
		return 2;
	}

	// 没有出错，但 cmds 为空，表示没有更多指令了
	if (!cmds) { return 0; }

	// 压入要执行的 SCPI 命令组
	lua_push_scpi_cmdgroup(L, cmds);
	return 1;
}

/**
* 解析结束，释放资源（包括解析的结果）
* @param yybuf scpi.parse_prepare() 返回的缓冲区
*/
static int _scpi_parse_finish(lua_State* L) {
	void *yybuf = lua_touserdata(L, 1);
	scpi_parse_finish(yybuf);
	return 0;
}

static const struct luaL_Reg _lib[] = {
	{ "parse_prepare", _scpi_parse_prepare },
	{ "parse_next", _scpi_parse_next },
	{ "parse_finish", _scpi_parse_finish },
	{ NULL, NULL },
};

int luaopen_scpi(lua_State* L) {
#if LUA_VERSION_NUM < 502
	luaL_register(L, "scpi", _lib);
#else
	luaL_newlib(L, _lib);
#endif
	return 1;
}
